package com.lagou.filter;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.apache.log4j.Logger;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Waiting
 * @date 2020/7/10 下午 01:31
 */
@Activate(group = CommonConstants.CONSUMER)
public class TpMonitorFilter implements Filter {

    private Logger logger = Logger.getLogger(this.getClass());

    private Map<String, Cache<String, Long>> methodCallTimeMap = new ConcurrentHashMap<>();


    public TpMonitorFilter() {

        Executors.newScheduledThreadPool(1).scheduleWithFixedDelay( () -> {

            logger.info("=========== 呼叫統計 ============");
            for (Map.Entry<String, Cache<String, Long>> methodCallEntry : methodCallTimeMap.entrySet()) {
                String methodName = methodCallEntry.getKey();
                Cache<String, Long> timeCache = methodCallEntry.getValue();
                List<Long> times = timeCache.asMap().values().stream().sorted().collect(Collectors.toList());

                if (!times.isEmpty()) {
                    int tp90Inex = times.size() * 90 / 100;
                    int tp99Inex = times.size() * 99 / 100;

                    logger.info("方法名: " + methodName + ", 1分鐘內總調用次數: " + times.size()
                            + ", TP90: " + times.get(tp90Inex) + ", TP99: " + times.get(tp99Inex));
                }

            }


        }, 0L, 5, TimeUnit.SECONDS);
    }


    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);

        if (async) {
            return invoker.invoke(invocation);
        } else {
            long time1 = 0L;

            try {
                time1 = System.currentTimeMillis();
                return invoker.invoke(invocation);
            } finally {
                long duration = System.currentTimeMillis() - time1;
                String methodName = invocation.getMethodName();
                Cache<String, Long> timeCache = methodCallTimeMap.computeIfAbsent(methodName,
                        m -> CacheBuilder.newBuilder()
                        .expireAfterWrite(60, TimeUnit.SECONDS).build());
                timeCache.put(UUID.randomUUID().toString(), duration);
            }
        }
    }
}
